package cn.maiyar.generator.util;

import cn.hutool.core.util.StrUtil;
import cn.maiyar.generator.constant.GeneratorConstant;
import cn.maiyar.generator.entity.CustomCode;
import cn.maiyar.generator.entity.CustomDataSource;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.builder.Controller;
import com.baomidou.mybatisplus.generator.config.builder.Entity;
import com.baomidou.mybatisplus.generator.config.builder.Mapper;
import com.baomidou.mybatisplus.generator.config.builder.Service;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 代码生成工具类
 *
 * @author c.c
 */
@Slf4j
@Component
public class GeneratorUtil {

    /**
     * 数据源配置
     *
     * @return
     */
    public DataSourceConfig dataSourceConfig(CustomCode customCode) {
        CustomDataSource dataSource = customCode.getDataSource();
        return new DataSourceConfig
                .Builder(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword())
//                .dbQuery(new MySqlQuery()) // 数据库查询
//                .schema("mybatis-plus") // 数据库 schema(部分数据库适用)
//                .typeConvert(new MySqlTypeConvert()) // 数据库类型转换器
//                .keyWordsHandler(new MySqlKeyWordsHandler()) // 数据库关键字处理器
                .build();
    }

    /**
     * 全局配置
     *
     * @return
     */
    public GlobalConfig globalConfig(CustomCode customCode) {
        GlobalConfig.Builder builder = new GlobalConfig.Builder();

        // 生成代码的地址
        String packageDir = customCode.getPackageDir();
        // 输出路径
        String outputDir = this.getOutputDir(packageDir);
        // 作者
        String author = customCode.getAuthor();
        // 是否启用swagger
        Boolean isSwagger2 = customCode.getIsSwagger();
        if (isSwagger2) {
            builder.enableSwagger();
        }
        // 是否覆盖已生成文件
        Boolean fileOverride = customCode.getFileOverride();
        if (fileOverride) {
            builder.fileOverride();
        }
        return builder
                // 禁止打开输出目录
//                .disableOpenDir()
                // 指定输出目录
                .outputDir(outputDir)
                // 作者名
                .author(author)
                // 开启 kotlin 模式
//                .enableKotlin()
                // 时间策略
                .dateType(DateType.TIME_PACK)
                // 注释日期
                .commentDate(GeneratorConstant.COMMENT_DATE)
                .build();
    }

    /**
     * 指定输出目录
     *
     * @return outputDir
     */
    public String getOutputDir(String packageDir) {
        return (StrUtil.isBlank(packageDir) ? GeneratorConstant.DEFAULT_DIR : packageDir)
//                + "/src/main/java"
                ;
    }

    /**
     * 包配置
     *
     * @return
     */
    public PackageConfig packageConfig(CustomCode customCode) {
        // 父包名
        String parentName = customCode.getParentName();
        // 父包模块名
        String moduleName = customCode.getModuleName();
        return new PackageConfig.Builder()
                // 父包名
                .parent(parentName)
                // 父包模块名
                .moduleName(moduleName)
                // Entity 包名
                .entity("entity")
                // Service 包名
                .service("service")
                // Service Impl 包名
                .serviceImpl("service.impl")
                // Mapper 包名
                .mapper("mapper")
                // Mapper XML 包名
                .xml("mapper.xml")
                // Mapper XML 包名
                .controller("controller")
                // 自定义文件包名
                .other("other")
                // 路径配置信息
//                .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "X://"))
                .build();
    }

    /**
     * 模板配置
     *
     * @return
     */
    public TemplateConfig templateConfig() {
        TemplateConfig.Builder tempBuilder = new TemplateConfig.Builder();
        return tempBuilder
                // 禁用模板
                .disable(TemplateType.ENTITY)
                // 设置实体模板路径(JAVA)
                .entity("/templates/entity.java")
                // 设置实体模板路径(kotlin)
//                .entityKt("/templates/entity.java")
                // 设置 service 模板路径
                .service("/templates/service.java")
                // 设置 serviceImpl 模板路径
                .serviceImpl("/templates/serviceImpl.java")
                // 设置 mapper 模板路径
                .mapper("/templates/mapper.java")
                // 设置 mapperXml 模板路径
                .mapperXml("/templates/mapper.xml")
                // 设置 controller 模板路径
                .controller("/templates/controller.java")
                .build();
    }

    /**
     * 策略配置
     *
     * @return
     */
    public StrategyConfig strategyConfig(CustomCode customCode) {
        // 策略配置
        StrategyConfig.Builder strategyBuilder = new StrategyConfig.Builder();
        // 需要生成的表名(两者只能取其一)
        String[] includeTables = customCode.getIncludeTables();
        if (includeTables.length > 0) {
            strategyBuilder.addInclude(includeTables);
        }
        // 需要排除的表名(两者只能取其一)
        String[] excludeTables = customCode.getExcludeTables();
        if (excludeTables.length > 0 && includeTables.length <= 0) {
            strategyBuilder.addExclude(excludeTables);
        }
        // 需要过滤掉的表前缀
        String[] tablePrefix = customCode.getTablePrefix();
        if (tablePrefix.length > 0) {
            strategyBuilder.addTablePrefix(tablePrefix);
        }
        // 需要过滤掉的表后缀
        String[] tableSuffix = customCode.getTableSuffix();
        if (tableSuffix.length > 0) {
            strategyBuilder.addTableSuffix(tableSuffix);
        }
        // 需要过滤掉的字段前缀
        String[] fieldPrefix = customCode.getFieldPrefix();
        if (fieldPrefix.length > 0) {
            strategyBuilder.addFieldPrefix(fieldPrefix);
        }
        // 需要过滤掉的字段后缀
        String[] fieldSuffix = customCode.getFieldSuffix();
        if (fieldSuffix.length > 0) {
            strategyBuilder.addFieldSuffix(fieldSuffix);
        }
        strategyBuilder
                // 	开启大写命名
                .enableCapitalMode()
                // 开启跳过视图
                .enableSkipView()
                // 	禁用 sql 过滤
                .disableSqlFilter()
                // 模糊表匹配(sql 过滤)
//                .likeTable(new LikeTable("USER"))
                // 	启用 schema
                .enableSchema();

        // 是否包含父类公共字段
        Boolean hasSuperEntity = customCode.getHasSuperEntity();
        // 父类公共字段
        String[] superEntityColumns = customCode.getSuperEntityColumns();

        // Entity 策略配置
        Entity.Builder entityBuilder = strategyBuilder
                .entityBuilder();
        if (hasSuperEntity) {
            entityBuilder
                    .superClass(GeneratorConstant.DEFAULT_ENTITY_SUPER_CLASS)
                    .addSuperEntityColumns(superEntityColumns);
        }
        entityBuilder
                // 	开启生成字段常量
//                .enableColumnConstant()
                // 开启链式模型
//                .enableChainModel()
                // 开启 Boolean 类型字段移除 is 前缀
                .enableRemoveIsPrefix()
                // 开启生成实体时生成字段注解
                .enableTableFieldAnnotation()
                // 数据库表映射到实体的命名策略 默认下划线转驼峰命名:NamingStrategy.underline_to_camel
                .naming(NamingStrategy.underline_to_camel)
                // 数据库表字段映射到实体的命名策略 默认为 null，未指定按照 naming 执行
                .columnNaming(NamingStrategy.underline_to_camel)
                // 添加忽略字段
//                .addIgnoreColumns("age")
                // 添加表字段填充
//                .addTableFills(new Column("create_time", FieldFill.INSERT))
//                .addTableFills(new Property("updateTime", FieldFill.INSERT_UPDATE))
                // 	全局主键类型
//                .idType(IdType.AUTO)
                // 	转换文件名称
//                .convertFileName()
                //  格式化文件名称
//                .formatFileName("%sEntity")
                // 开启 lombok 模型
                .enableLombok()
                .build();

        // Controller 策略配置
        Controller.Builder controllerBuilder = strategyBuilder
                .controllerBuilder();
        controllerBuilder
                // 设置父类
                .superClass(GeneratorConstant.DEFAULT_CONTROLLER_SUPER_CLASS)
                // 开启驼峰转连字符
                .enableHyphenStyle()
                // 开启生成@RestController 控制器
                .enableRestStyle()
                // 格式化文件名称
//                .formatFileName("%sAction")
                .build();

        // Service 策略配置
        Service.Builder serviceBuilder = strategyBuilder
                .serviceBuilder();
        if (hasSuperEntity) {
            serviceBuilder
                    .superServiceClass(GeneratorConstant.DEFAULT_SERVICE_SUPER_CLASS)
                    .superServiceImplClass(GeneratorConstant.DEFAULT_SERVICE_IMPL_SUPER_CLASS);
        } else {
            serviceBuilder
                    .superServiceClass(GeneratorConstant.MYBATIS_SERVICE_SUPER_CLASS)
                    .superServiceImplClass(GeneratorConstant.MYBATIS_SERVICE_IMPL_SUPER_CLASS);
        }
        serviceBuilder
                .formatServiceFileName("%sService")
                .formatServiceImplFileName("%sServiceImp")
                .build();

        // Mapper 策略配置
        Mapper.Builder mapperBuilder = strategyBuilder
                .mapperBuilder();
        mapperBuilder
                // 设置父类
//                .superClass("")
                // 开启 @Mapper 注解
                .enableMapperAnnotation()
                // 启用 BaseResultMap 生成
                .enableBaseResultMap()
                // 启用 BaseColumnList
                .enableBaseColumnList()
                // 设置缓存实现类
//                .cache(MyMapperCache.class)
                // 转换 mapper 类文件名称
//                .convertMapperFileName()
                // 转换 xml 文件名称
//                .convertXmlFileName()
                // 格式化 mapper 文件名称
//                .formatMapperFileName("%sDao")
                // 格式化 xml 实现类文件名称
//                .formatXmlFileName("%sXml")
                .build();

        return strategyBuilder.build();
    }


    /**
     * 注入配置
     *
     * @return
     */
    public InjectionConfig injectionConfig(CustomCode customCode) {
        // 系统名称
        String systemName = customCode.getSystemName();
        // 模块名称
        String moduleName = customCode.getModuleName();
        // 服务名称
        String serviceName = customCode.getServiceName();
        // 是否包含包装器
        Boolean hasWrapper = customCode.getHasWrapper();
        // 租户字段
        String tenantColumn = customCode.getTenantColumn();
        // 生成代码的地址
        String packageDir = customCode.getPackageDir();
        // 输出路径
        String outputDir = this.getOutputDir(packageDir);

        InjectionConfig.Builder injectionBuilder = new InjectionConfig.Builder();
        injectionBuilder
                // 输出文件之前消费者
                .beforeOutputFile((tableInfo, objectMap) -> {
                    // 表名称
                    String name = tableInfo.getName();
                    // 实体对象名称
                    String entityName = tableInfo.getEntityName();

                    objectMap.put("systemName" , systemName);
                    objectMap.put("serviceName" , serviceName);
                    objectMap.put("moduleName" , moduleName);
                    objectMap.put("tenantColumn" , tenantColumn);
                    objectMap.put("hasWrapper" , hasWrapper);
                    objectMap.put("entityKey" , (
                            name.replace("mr_" , "")
                                    .replace(StringPool.UNDERSCORE, StringPool.SLASH))
                    );
                    log.debug("输出路径: {}, 服务名称: {}, 模块名称: {} , 表名称: {} , 实体对象名称: {} " , outputDir, serviceName, moduleName, name, entityName);
                })
                // 自定义配置 Map 对象
                .customMap(
                        Map.of()
                )
                // 自定义配置模板文件
//                .customFile(Collections.singletonMap("sql.txt", "/templates/sql/menu.sql.vm"))
                .customFile(
                        Map.of(
                                "VO" , "/templates/entityVO.java.vm" ,
                                "DTO" , "/templates/entityDTO.java.vm" ,
                                "Wrapper" , "/templates/wrapper.java.vm" ,

                                "api" , "/templates/vue/api.js.vm" ,
                                "views" , "/templates/vue/crud.vue.vm"
                        )
                )
                .build();
        return injectionBuilder.build();
    }


    /**
     * 重写模板自定义输出
     *
     * @return
     */
    public VelocityTemplateEngine velocityTemplateEngine() {

        return new VelocityTemplateEngine() {

            @Override
            protected void outputCustomFile(Map<String, String> customFile, TableInfo tableInfo, Map<String, Object> objectMap) {
                String entityName = tableInfo.getEntityName();
                String otherPath = getPathInfo(OutputFile.other);

                customFile.forEach((key, value) -> {
                    String filePrefix = otherPath + File.separator + entityName.toLowerCase() + File.separator;
                    String fileSuffix = "";
                    String fileName = filePrefix + String.format("%s" , entityName + key) + fileSuffix;

                    switch (key) {
                        case "VO":
                        case "DTO":
                        case "Wrapper":
                            filePrefix = otherPath + File.separator + key.toLowerCase() + File.separator;
                            fileSuffix = StringPool.DOT_JAVA;
                            fileName = filePrefix + String.format("%s" , entityName + key) + fileSuffix;
                            break;
                        case "api":
                            filePrefix = otherPath + File.separator + "vue" + File.separator + key + File.separator;
                            fileSuffix = GeneratorConstant.DOT_JS;
                            fileName = filePrefix + String.format("%s" , entityName) + fileSuffix;
                            break;
                        case "views":
                            filePrefix = otherPath + File.separator + "vue" + File.separator + key + File.separator;
                            fileSuffix = GeneratorConstant.DOT_VUE;
                            fileName = filePrefix + String.format("%s" , entityName) + fileSuffix;
                            break;
                    }
                    outputFile(new File(fileName), objectMap, value);
                });
            }
        };
    }

    /**
     * 代码生成
     *
     * @param customCode
     */
    public void run(CustomCode customCode) {
        new AutoGenerator(this.dataSourceConfig(customCode))
                // 全局配置
                .global(this.globalConfig(customCode))
                // 包配置
                .packageInfo(this.packageConfig(customCode))
                // 策略配置
                .strategy(this.strategyConfig(customCode))
                // 注入配置
                .injection(this.injectionConfig(customCode))
                // 模板配置
                .template(this.templateConfig())
                // 执行
                .execute(velocityTemplateEngine())
        ;
    }
}
